import com.sun.tools.attach.VirtualMachine;
import exception.NotDirectoryException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.File;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.List;

public class HotUpdateTask extends Thread{
    private boolean stop;
    private String listeningPath;
    private Log log = LogFactory.getLog(getClass().getName());
    private List<HotUpdateHistory> histories = new ArrayList<>();

    private boolean inHistory(File file) {
        return histories.stream()
                .anyMatch(h -> h.getClassName().equals(file.getName()) && h.getLastModify() == file.lastModified());
    }

    private class HotUpdateHistory {
        private String className;
        private long lastModify;
        public HotUpdateHistory(String className, long lastModify) {
            this.className = className;
            this.lastModify = lastModify;
        }
        public String getClassName() {
            return className;
        }
        public long getLastModify() {
            return lastModify;
        }
    }

    public HotUpdateTask(String listeningPath) {
        this.listeningPath = listeningPath;
    }

    private String pid() {
        String name = ManagementFactory.getRuntimeMXBean().getName();
        return name.substring(0, name.indexOf("@"));
    }


    public void shutDown() {
        this.stop = true;
    }

    @Override
    public void run() {
        File path = new File(listeningPath);
        if (!path.isDirectory()) {
            throw new NotDirectoryException();
        }
        while (!stop) {
            try {
                check(path);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private void check(File path) {
        File[] files = path.listFiles();
        if (files != null && files.length > 0) {
            StringBuilder sb = new StringBuilder();
            sb.append(listeningPath).append(";");
            boolean hasNewClass = false;
            for (File file : files) {
                if (file.getName().endsWith(".class") && !inHistory(file)) {
                    hasNewClass = true;
                    sb.append(file.getName()).append(",");
                    histories.add(new HotUpdateHistory(file.getName(), file.lastModified()));
                }
            }
            if (hasNewClass) {
                VirtualMachine vm = null;
                try {
                    vm = VirtualMachine.attach(pid());
                    vm.loadAgent(HotUpdateAgent.class.getProtectionDomain().getCodeSource().getLocation().getPath(), sb.toString());
                } catch (Exception e) {
                    log.error(e);
                }
                finally {
                    if (vm != null) {
                        try {
                            vm.detach();
                        } catch (IOException e) {
                            log.error(e);
                        }
                    }
                }
            }
        }
    }
}
